package weka;

import java.util.Random;

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.lazy.IBk;
import weka.classifiers.trees.J48;
import weka.classifiers.trees.J48_KNN;
import weka.core.Instances;
import weka.core.Utils;
import weka.core.converters.ConverterUtils.DataSource;

public class ClassifiersTester implements ClassifiersInterface {
	InterfaceReturnValue ret = new InterfaceReturnValue();
	@Override
	public InterfaceReturnValue testClassifierInsts(Instances inst,
			String classifier_name, String classifier_opt) {
		
		setAttributesNames(inst);
		
		setClassifier(classifier_name, classifier_opt);
		if(ret.hadError()){ return ret; }
		
		try {
		runClassifier(inst);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("Had Error during running the classifier: " + e.getMessage());
			e.printStackTrace();
		}
		if(ret.hadError()){ return ret; }
		try {
			testClassifier();
			} catch (Exception e) {
				ret.setHadError(true);
				ret.setErrorDescription("Had Error during running the classifier: " + e.getMessage());
				e.printStackTrace();
			}
		return ret;
	}

	private void setAttributesNames(Instances inst) {
		String[] attr_names = new String[inst.numAttributes()];
		for(int i=0; i<inst.numAttributes(); ++i){
			attr_names[i] = inst.attribute(i).name();
		}
		ret.setTargetClassLabels(attr_names);
	}

	private void testClassifier() {
		Evaluation eval = ret.getEvaluation();
		
		ret.setErrorRate(eval.errorRate());
		ret.setAccuracy(1.0-eval.errorRate());
		ret.setConfusionMatrix(eval.confusionMatrix());
		
	}

	private void runClassifier(Instances data) throws Exception {
	
		if(null == data){
			ret.setHadError(true);
			ret.setErrorDescription("empty instances in runClassifier");
			return;
		}
		ret.getClassifier().buildClassifier(data);
		ret.setEvaluation(new Evaluation(data)); 
		ret.getEvaluation().crossValidateModel(ret.getClassifier(), data, 10, new Random(1));

		
	}

	private void setClassifier(String classifier_name,
			String classifier_opt) {
		Classifier classifier = null;
		if(classifier_name.toUpperCase().contentEquals("KNN")){
			classifier = getKNN(classifier_opt);
		}else
		if(classifier_name.toUpperCase().contentEquals("J48KNN")){
			classifier =  getJR48(classifier_opt);
		}else
		if(classifier_name.toUpperCase().contentEquals("J48")){
			classifier =  getJ48KNN(classifier_opt);
		}else{
			try {
				Class c =  Class.forName(classifier_name);
				if(Classifier.class.isAssignableFrom(c)){
					classifier = (Classifier)c.newInstance();
					String[] options = weka.core.Utils.splitOptions(classifier_opt);
					classifier.setOptions(options);
				}
			} catch (Exception e) {
				classifier = null;
			}
			
		}
		if(null == classifier){
			ret.setHadError(true);
			ret.setErrorDescription("couldn't find the requested interface");
		}
		ret.setClassifier(classifier)
;	}

	private Classifier getJR48(String classifier_opt) {
		String[] options;
		J48 classifier = null;
		try {
			if(null == classifier_opt){
				options = weka.core.Utils.splitOptions("-U -M 10 -L -P");
			}else{
				options = weka.core.Utils.splitOptions(classifier_opt);
			}
			classifier = new J48();
			classifier.setOptions(options);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("Had Error during construction of J48: " + e.getMessage());

			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return classifier;
	}

	private Classifier getKNN(String classifier_opt) {
		IBk classifier = null;

		try {
		String[] user_options = null;
		if(null != classifier_opt){
			user_options = weka.core.Utils.splitOptions(classifier_opt);
		}

		int k_for_knn = 10;
	    String k_for_knn_string = Utils.getOption('K', user_options);
	    if (k_for_knn_string.length() != 0) {
	      k_for_knn = Integer.parseInt(k_for_knn_string);
	    }

	    
		String properties_to_use = Utils.getOption('P', user_options);
	    if (properties_to_use.length() != 0) {
	    	properties_to_use = "-R " + properties_to_use;
		    }
		

		String[] options;
		
			 options = weka.core.Utils
					.splitOptions("-K "
							+ k_for_knn
							+ " -W 0 -A \"weka.core.neighboursearch.LinearNNSearch -A "
							+"\\\"weka.core.EuclideanDistance "
							+ properties_to_use
							+" \\\"\"");
			classifier = new IBk();
			classifier.setOptions(options);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("Had Error during construction of KNN" + e.getMessage());

			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return classifier;
	}


	private Classifier getJ48KNN(String classifier_opt) {
		String[] options;
		J48_KNN classifier = null;
		try {
			if(null == classifier_opt){
				options = weka.core.Utils.splitOptions("-U -M 10 -L -P -K 12");
			}else{
				options = weka.core.Utils.splitOptions(classifier_opt);
			}
			classifier = new J48_KNN();
			classifier.setOptions(options);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("Had Error during construction of J48KNN" + e.getMessage());

			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return classifier;
	}

	@Override
	public InterfaceReturnValue testClassifierOnDatabase(
			String database_source, String classifier_name,
			String classifier_opt) {
		
		DataSource source;
		try {
			source = new DataSource(database_source);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("had an error getting the database from " + 
			database_source + ":" + e.getMessage());
			e.printStackTrace();
			return ret;
		} 
		Instances data;
		try {
			data = source.getDataSet();
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("had an error getting the instances from database from " + 
					database_source + ":" + e.getMessage());
			e.printStackTrace();
			return ret;
		} 
		data.setClassIndex(data.numAttributes() - 1);
		return testClassifierInsts(data, classifier_name, classifier_opt);
	}
	
	public InterfaceReturnValue testClassifierOnDataSet(String database_sources, String classifier_name, String classifier_opt){
		
		/*DataSource source;
		try {
			Director
			source = new DataSource(database_source);
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("had an error getting the database from " + 
			database_source + ":" + e.getMessage());
			e.printStackTrace();
			return ret;
		} 
		Instances data;
		try {
			data = source.getDataSet();
		} catch (Exception e) {
			ret.setHadError(true);
			ret.setErrorDescription("had an error getting the instances from database from " + 
					database_source + ":" + e.getMessage());
			e.printStackTrace();
			return ret;
		} 
		data.setClassIndex(data.numAttributes() - 1);
		return testClassifierInsts(data, classifier_name, classifier_opt);*/
		return null;
	}
	
}
